﻿namespace Sidvall.CodeGenerator
{
    public class DomainObjectRelation
    {
        #region Business Members

        public string Name { get; set; }
        public string NameInDataStore { get; set; }
        public DomainObject SourceObject { get; private set; }
        public DomainObject RelatedObject { get; private set; }
        public Multiplicity SourceObjectMultiplicity { get; set; }
        public Multiplicity RelatedObjectMultiplicity { get; set; }
        public DomainObjectRelationFieldCollection Fields { get; private set; }
        public int? RelatedObjectMaxOccurs { get; set; }
        public int? RelatedObjectMinOccurs { get; set; }
        public int? SourceObjectMaxOccurs { get; set; }
        public int? SourceObjectMinOccurs { get; set; }
        public bool NameIsPlural { get; set; }
        public bool IsDatabaseOnly { get; set; }
        public bool CascadeDelete { get; set; }

        #region Description

        private string _Description = null;
        public string Description
        {
            get
            {
                if (_Description == null)
                    _Description = ToString();
                return _Description;
            }
        }

        #endregion
        #region FullName

        public string FullName
        {
            get
            {
                return GetFullName(this.SourceObject.FullName, this.Name);
            }
        }

        #endregion
        #region Key

        public string Key
        {
            get
            {
                return GetKey(this);
            }
        }

        #endregion
        #region PluralName

        public string PluralName
        {
            get
            {
                if (this.NameIsPlural)
                    return this.Name;
                return TypeMapper.GetPluralName(this.Name);
            }
        }

        #endregion
        #region RelationType

        public RelationType RelationType
        {
            get
            {
                switch (this.SourceObjectMultiplicity)
                {
                    case Multiplicity.ZeroOrOne:
                        switch (this.RelatedObjectMultiplicity)
                        {
                            case Multiplicity.ZeroOrOne:
                                return CodeGenerator.RelationType.OneToOne;
                            case Multiplicity.One:
                                return CodeGenerator.RelationType.ChildParent;
                            default:
                                return CodeGenerator.RelationType.ParentChild;
                        }
                    case Multiplicity.One:
                        switch (this.RelatedObjectMultiplicity)
                        {
                            case Multiplicity.ZeroOrOne:
                                return CodeGenerator.RelationType.ParentChild;
                            case Multiplicity.One:
                                return CodeGenerator.RelationType.OneToOne;
                            default:
                                return CodeGenerator.RelationType.ParentChild;
                        }
                    default:
                        switch (this.RelatedObjectMultiplicity)
                        {
                            case Multiplicity.ZeroOrOne:
                                return CodeGenerator.RelationType.ChildParent;
                            case Multiplicity.One:
                                return CodeGenerator.RelationType.ChildParent;
                            default:
                                return CodeGenerator.RelationType.ManyToMany;
                        }
                }
            }
        }

        #endregion
        #region RelatedObjectIsCollection

        public bool RelatedObjectIsCollection
        {
            get
            {
                return (this.RelatedObjectMultiplicity == Multiplicity.Many);
            }
        }

        #endregion
        #region RelatedObjectRelation

        private DomainObjectRelation _RelatedObjectRelation;
        public DomainObjectRelation RelatedObjectRelation
        {
            get
            {
                int iMax;
                bool invalid;

                if (_RelatedObjectRelation == null)
                {
                    if (this.RelatedObject == null)
                        return null;
                    foreach (var relatedRelation in this.RelatedObject.Relations)
                    {
                        if (relatedRelation.RelatedObject.Name != this.SourceObject.Name)
                            continue;
                        if (relatedRelation.Fields.Count != this.Fields.Count)
                            continue;
                        iMax = relatedRelation.Fields.Count;
                        invalid = false;
                        for (int i = 0; i < iMax; i++)
                        {
                            if (relatedRelation.Fields[i].RelatedProperty.Name != this.Fields[i].SourceProperty.Name)
                            {
                                invalid = true;
                                break;
                            }
                            if (relatedRelation.Fields[i].SourceProperty.Name != this.Fields[i].RelatedProperty.Name)
                            {
                                invalid = true;
                                break;
                            }
                        }
                        if (!invalid)
                        {
                            _RelatedObjectRelation = relatedRelation;
                            return _RelatedObjectRelation;
                        }
                    }
                }
                return _RelatedObjectRelation;
            }
        }

        #endregion
        #region SourceObjectIsCollection

        public bool SourceObjectIsCollection
        {
            get
            {
                return (this.SourceObjectMultiplicity == Multiplicity.Many);
            }
        }

        #endregion

        #region GetFullName

        public static string GetFullName(string className, string name)
        {
            if (string.IsNullOrWhiteSpace(className))
                return name;
            return string.Join(".", className, name);
        }

        #endregion
        #region GetKey

        public static string GetKey(DomainObjectRelation item)
        {
            if (item == null)
                throw new System.ArgumentNullException("item");

            return item.Name;
        }

        #endregion
        #region GetRelatedProperties

        public System.Collections.ObjectModel.Collection<DomainObjectProperty> GetRelatedProperties(DomainObjectProperty sourceProperty)
        {
            System.Collections.ObjectModel.Collection<DomainObjectProperty> items;

            if (sourceProperty == null)
                throw new System.ArgumentNullException("sourceProperty");

            items = new System.Collections.ObjectModel.Collection<DomainObjectProperty>();
            foreach (var item in this.Fields)
            {
                if (item.SourceProperty.Name == sourceProperty.Name)
                    items.Add(item.RelatedProperty);
            }

            return items;
        }

        #endregion
        #region Import

        public void Import(DomainObjectRelation item)
        {
            if (item == null)
                return;

            if (item.Name != null)
                this.Name = item.Name;
            if (item.NameInDataStore != null)
                this.NameInDataStore = item.NameInDataStore;
        }

        #endregion
        #region RelatedObjectMaxOccursResolved

        public int? RelatedObjectMaxOccursResolved
        {
            get
            {
                if (this.RelatedObjectMaxOccurs != null)
                    return this.RelatedObjectMaxOccurs;
                switch (this.RelatedObjectMultiplicity)
                {
                    case Multiplicity.ZeroOrOne:
                    case Multiplicity.One:
                        return 1;
                    default:
                        return null;
                }
            }
        }

        #endregion
        #region RelatedObjectMinOccursResolved

        public int? RelatedObjectMinOccursResolved
        {
            get
            {
                if (this.RelatedObjectMinOccurs != null)
                    return this.RelatedObjectMinOccurs;
                switch (this.RelatedObjectMultiplicity)
                {
                    case Multiplicity.One:
                        return 1;
                    default:
                        return 0;
                }
            }
        }

        #endregion
        #region RelatedObjectTypeName

        public string RelatedObjectTypeName
        {
            get
            {
                if (this.RelatedObjectMultiplicity == Multiplicity.Many)
                    return this.RelatedObject.CollectionTypeName;
                else
                    return this.RelatedObject.TypeName;
            }
        }

        #endregion
        #region SourceObjectMaxOccursResolved

        public int? SourceObjectMaxOccursResolved
        {
            get
            {
                if (this.SourceObjectMaxOccurs != null)
                    return this.SourceObjectMaxOccurs;
                switch (this.SourceObjectMultiplicity)
                {
                    case Multiplicity.ZeroOrOne:
                    case Multiplicity.One:
                        return 1;
                    default:
                        return null;
                }
            }
        }

        #endregion
        #region SourceObjectMinOccursResolved

        public int? SourceObjectMinOccursResolved
        {
            get
            {
                if (this.SourceObjectMinOccurs != null)
                    return this.SourceObjectMinOccurs;
                switch (this.SourceObjectMultiplicity)
                {
                    case Multiplicity.One:
                        return 1;
                    default:
                        return 0;
                }
            }
        }

        #endregion
        #region SourceObjectTypeName

        public string SourceObjectTypeName
        {
            get
            {
                if (this.SourceObjectMultiplicity == Multiplicity.Many)
                    return this.SourceObject.CollectionTypeName;
                else
                    return this.SourceObject.TypeName;
            }
        }

        #endregion
        #region ToString

        public override string ToString()
        {
            if (this.Fields.Count == 1)
                return this.SourceObject.Name + "-->" + this.RelatedObject.Name + " (" + this.Fields[0].ToString() + ")";
            return this.SourceObject.Name + "-->" + this.RelatedObject.Name;
        }

        #endregion

        #endregion
        #region Constructors

        public DomainObjectRelation(DomainObject sourceObject, DomainObject relatedObject)
        {
            this.SourceObject = sourceObject;
            this.RelatedObject = relatedObject;
            this.Fields = new DomainObjectRelationFieldCollection();
        }

        #endregion
    }
}
